package middleware

import (
	"crypto/sha1"
	"encoding/hex"
	"net/http"
	"fmt"
	"github.com/gin-gonic/gin"
	"go-base/hs-gin/conf"
	"go-base/hs-gin/controller"
	"sort"
	"strconv"
	"time"
)

//设置跨域
func AllowOrigin() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		ctx.Header("Access-Control-Allow-Origin", "*")                                       // 这是允许访问所有域
		ctx.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") //服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求
		//  header的类型
		ctx.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma, hallToken, hall-session, auth_token, authtoken")
		//              允许跨域设置                                                                                                      可以返回其他子段
		ctx.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
		ctx.Header("Access-Control-Max-Age", "172800")                                                                                                                                                           // 缓存请求信息 单位为秒
		ctx.Header("Access-Control-Allow-Credentials", "true")                                                                                                                                                  //  跨域请求是否需要带cookie信息 默认设置为true
		ctx.Set("content-type", "application/json")
		method := ctx.Request.Method
		if method == "OPTIONS" {
			ctx.AbortWithStatus(http.StatusNoContent)
		}
	}
}

//请求时间和白名单
//func ReqCheckTime() gin.HandlerFunc {
//	return func(ctx *gin.Context) {
//		if b := tools.Contains(WhiteRoute, ctx.Request.URL.Path); b {
//			return
//		}
//		ip := ctx.ClientIP()
//		if b := ClientCheck(ip); !b {
//			log.Warn("ReqCheckTime=>%v, ClientCheck=>[%v]", ctx.Request.URL, ip)
//			ctx.JSON(http.StatusOK, form.OutputBase{
//				RetCode: msg.EC_PARAM,
//				RetMsg: "非法请求",
//			})
//			ctx.Abort()
//			return
//		}
//		var timeStr string
//		if ctx.Request.Method == http.MethodPost {
//			return
//		} else {
//			timeStr = ctx.Query("timestamp")
//		}
//		recvWindow := viper.GetInt64("common.validTime")
//
//		timestamp, err := strconv.ParseInt(timeStr, 10, 64)
//		if err != nil {
//			log.Warn("ReqCheckTime=>%v, ParseInt error=>[%v]", ctx.Request.URL, timeStr)
//			ctx.JSON(http.StatusOK, form.OutputBase{
//				RetCode: msg.EC_PARAM,
//				RetMsg: "参数错误",
//			})
//			ctx.Abort()
//			return
//		}
//		serverTime := time.Now().UnixNano()/1e6
//		if !((timestamp < (serverTime + 1000000)) && (serverTime - timestamp) <= recvWindow) {
//			fmt.Println(fmt.Sprintf("ReqCheckTime=>%v, time error=>[serverTime=%v, timestamp=%v]", ctx.Request.URL, serverTime, timestamp))
//			log.Warning("ReqCheckTime=>%v, time error=>[serverTime=%v, timestamp=%v]", ctx.Request.URL, serverTime, timestamp)
//			ctx.JSON(http.StatusOK, form.OutputBase{
//				RetCode: msg.EC_VALID_TIME,
//				RetMsg: "时间已过期",
//			})
//			ctx.Abort()
//			return
//		}
//		ctx.Next()
//		return
//	}
//}

// 验证参数一致
func CheckParamUnanimous() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		// 获取所有参数
		ctx.Request.ParseForm()
		ctx.Request.ParseMultipartForm(32 << 20) // 32M
		params := ctx.Request.Form

		// 获取用户提交的签名
		formSign := ctx.GetHeader("sign")
		if formSign == "" {
			formSign = params.Get("sign")
		}
		if formSign == "" {
			new(controller.Controller).RespErr(ctx, "缺少参数sign")

			ctx.Abort()
			return
		}
		// 签名不参与加密
		params.Del("sign")

		// 参数排序
		paramKeys := make([]string, len(params))

		i := 0
		for k := range params {
			paramKeys[i] = k
			i++
		}

		sort.Strings(paramKeys)

		// 拼成：key1=value2&key2=value2&secret=xxx
		var paramStr string
		for _, v := range paramKeys {
			paramStr += fmt.Sprintf("%s=%s&", v, params.Get(v))
		}
		// 最后拼上secret
		paramStr += fmt.Sprintf("secret=%s", conf.Configer.ApiConf.ParamSecret)

		// 把拼接的参数签名后进行对比
		s := sha1.New()
		s.Write([]byte(paramStr))
		sign := hex.EncodeToString(s.Sum(nil))

		// 打印出签名，方便调试
		if gin.Mode() != gin.ReleaseMode {
			fmt.Println("服务器生成的sign：", sign)
		}

		// 对比服务器生成的签名和用户提交的签名
		if sign != formSign {
			new(controller.Controller).RespErr(ctx, "参数不一致")

			ctx.Abort()
			return
		}

		//如果传了timestamp，检查sign有效期
		timestamp := params.Get("timestamp")
		if timestamp != "" {
			timestampInt, err := strconv.ParseInt(timestamp, 10, 64)
			if err != nil {
				new(controller.Controller).RespErr(ctx, "参数timestamp格式错误")

				ctx.Abort()
				return
			}

			if time.Now().Unix()-timestampInt > 30 { //有效期30分钟
				new(controller.Controller).RespErr(ctx, "sign已失效")

				ctx.Abort()
				return
			}
		}

		ctx.Next()
	}
}
